import os
import subprocess
import sys
import tempfile
import threading
import tkinter as tk
import traceback
from datetime import datetime
from tkinter import ttk, filedialog, messagebox, scrolledtext

import ttkbootstrap as ttk
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


@annotate(ClassInfo(name="PyCodeView", category="dev_tool"))
class PyCodeView(View):
    """关于视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 初始化变量
        self.current_file = None
        self.modified = False
        self.executing = False
        self.python_interpreter = sys.executable  # 获取当前Python解释器路径

        # 创建UI
        self._create_widgets()

        # 绑定窗口关闭事件
        # self.protocol("WM_DELETE_WINDOW", self.on_close)

    def _create_widgets(self):
        """创建界面组件"""
        # 主容器
        main_container = ttk.Frame(self, padding=10)
        main_container.pack(fill=BOTH, expand=YES)

        # 工具栏
        toolbar_frame = ttk.Frame(main_container)
        toolbar_frame.pack(fill=X, pady=5)

        # 文件操作按钮
        ttk.Button(
            toolbar_frame,
            text="新建",
            command=self.new_file,
            bootstyle=INFO,
            width=8
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            toolbar_frame,
            text="打开",
            command=self.open_file,
            bootstyle=PRIMARY,
            width=8
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            toolbar_frame,
            text="保存",
            command=self.save_file,
            bootstyle=SUCCESS,
            width=8
        ).pack(side=LEFT, padx=2)

        ttk.Button(
            toolbar_frame,
            text="另存为",
            command=self.save_file_as,
            bootstyle=SUCCESS,
            width=8
        ).pack(side=LEFT, padx=2)

        ttk.Separator(toolbar_frame, orient=VERTICAL).pack(side=LEFT, padx=5, fill=Y)

        # 执行控制按钮
        self.run_btn = ttk.Button(
            toolbar_frame,
            text="运行",
            command=self.run_script,
            bootstyle=DANGER,
            width=8
        )
        self.run_btn.pack(side=LEFT, padx=2)

        self.stop_btn = ttk.Button(
            toolbar_frame,
            text="停止",
            command=self.stop_script,
            bootstyle=WARNING,
            width=8,
            state=DISABLED
        )
        self.stop_btn.pack(side=LEFT, padx=2)

        ttk.Separator(toolbar_frame, orient=VERTICAL).pack(side=LEFT, padx=5, fill=Y)

        # Python解释器选择
        ttk.Label(toolbar_frame, text="Python解释器:").pack(side=LEFT, padx=5)
        self.interpreter_var = tk.StringVar(value=self.python_interpreter)
        ttk.Entry(
            toolbar_frame,
            textvariable=self.interpreter_var,
            width=30
        ).pack(side=LEFT, padx=5, fill=X, expand=YES)

        ttk.Button(
            toolbar_frame,
            text="浏览",
            command=self.select_interpreter,
            bootstyle=SECONDARY,
            width=6
        ).pack(side=LEFT, padx=2)

        # 主内容区域（分割为编辑器和日志）
        content_paned = ttk.PanedWindow(main_container, orient=HORIZONTAL)
        content_paned.pack(fill=BOTH, expand=YES, pady=5)

        # 代码编辑器区域
        editor_frame = ttk.LabelFrame(content_paned, text="代码编辑区", padding=5)
        content_paned.add(editor_frame, weight=1)

        self.code_editor = scrolledtext.ScrolledText(
            editor_frame,
            wrap=tk.WORD,
            font=("Consolas", 10)
        )
        self.code_editor.pack(fill=BOTH, expand=YES)

        # 绑定文本变化事件，标记为已修改
        self.code_editor.bind("<<Modified>>", self.on_text_modified)

        # 日志显示区域
        log_frame = ttk.LabelFrame(content_paned, text="执行日志", padding=5)
        content_paned.add(log_frame, weight=1)

        self.log_text = scrolledtext.ScrolledText(
            log_frame,
            wrap=tk.WORD,
            state=DISABLED,
            font=("Consolas", 10)
        )
        self.log_text.pack(fill=BOTH, expand=YES)

        # 配置日志文本标签样式
        self.log_text.tag_configure("info", foreground="white")
        self.log_text.tag_configure("success", foreground="#00ff00")
        self.log_text.tag_configure("warning", foreground="#ffff00")
        self.log_text.tag_configure("error", foreground="#ff0000")
        self.log_text.tag_configure("time", foreground="#00ffff")
        self.log_text.tag_configure("command", foreground="#ffaa00")

        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(
            self,
            textvariable=self.status_var,
            relief=SUNKEN,
            anchor=W
        )
        status_bar.pack(side=BOTTOM, fill=X)

    def log(self, message, level="info"):
        """添加日志信息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log_text.config(state=NORMAL)
        self.log_text.insert(tk.END, f"[{timestamp}] ", "time")
        self.log_text.insert(tk.END, f"{message}\n", level)
        self.log_text.config(state=DISABLED)
        self.log_text.see(tk.END)  # 滚动到最新日志
        self.update_status(message)

    def update_status(self, message):
        """更新状态栏信息"""
        self.status_var.set(message)

    def on_text_modified(self, event):
        """当文本被修改时触发"""
        if not self.modified:
            self.modified = True
        # 重置修改标记，以便下次修改时再次触发
        self.code_editor.edit_modified(False)

    def new_file(self):
        """创建新文件"""
        # 检查是否有未保存的修改
        if self.modified:
            response = messagebox.askyesnocancel(
                "保存提示",
                "当前文件有未保存的修改，是否保存？"
            )
            if response is None:  # 取消
                return
            if response:  # 是
                if not self.save_file():
                    return

        # 清空编辑器
        self.code_editor.delete("1.0", tk.END)
        self.current_file = None
        self.modified = False
        self.log("创建了新文件")

    def open_file(self):
        """打开文件"""
        # 检查是否有未保存的修改
        if self.modified:
            response = messagebox.askyesnocancel(
                "保存提示",
                "当前文件有未保存的修改，是否保存？"
            )
            if response is None:  # 取消
                return
            if response:  # 是
                if not self.save_file():
                    return

        # 打开文件选择对话框
        file_path = filedialog.askopenfilename(
            title="打开Python文件",
            filetypes=[("Python文件", "*.py"), ("所有文件", "*.*")]
        )

        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()

                # 显示文件内容
                self.code_editor.delete("1.0", tk.END)
                self.code_editor.insert(tk.END, content)

                self.current_file = file_path
                self.modified = False
                self.log(f"已打开文件: {os.path.basename(file_path)}")
            except Exception as e:
                self.log(f"打开文件失败: {str(e)}", "error")
                messagebox.showerror("错误", f"打开文件失败:\n{str(e)}")

    def save_file(self):
        """保存文件"""
        if self.current_file:
            try:
                content = self.code_editor.get("1.0", tk.END)
                with open(self.current_file, 'w', encoding='utf-8') as f:
                    f.write(content)

                self.modified = False
                self.log(f"已保存文件: {os.path.basename(self.current_file)}")
                return True
            except Exception as e:
                self.log(f"保存文件失败: {str(e)}", "error")
                messagebox.showerror("错误", f"保存文件失败:\n{str(e)}")
                return False
        else:
            return self.save_file_as()

    def save_file_as(self):
        """另存为文件"""
        default_filename = "untitled.py"
        if self.current_file:
            default_filename = os.path.basename(self.current_file)

        file_path = filedialog.asksaveasfilename(
            title="保存Python文件",
            defaultextension=".py",
            filetypes=[("Python文件", "*.py"), ("所有文件", "*.*")],
            initialfile=default_filename
        )

        if file_path:
            self.current_file = file_path
            return self.save_file()
        return False

    def select_interpreter(self):
        """选择Python解释器"""
        initial_dir = os.path.dirname(self.python_interpreter)

        interpreter_path = filedialog.askopenfilename(
            title="选择Python解释器",
            initialdir=initial_dir,
            filetypes=[
                ("Python解释器", "python.exe;python3.exe;python;python3"),
                ("可执行文件", "*.*")
            ]
        )

        if interpreter_path:
            self.python_interpreter = interpreter_path
            self.interpreter_var.set(interpreter_path)
            self.log(f"已选择Python解释器: {os.path.basename(interpreter_path)}")

    def run_script(self):
        """运行行脚本"""
        if self.executing:
            return

        # 检查是否有内容
        content = self.code_editor.get("1.0", tk.END).strip()
        if not content:
            messagebox.showwarning("警告", "脚本内容为空，无法执行")
            return

        # 保存文件
        if self.modified:
            if not self.save_file():
                return

        # 准备执行
        self.executing = True
        self.run_btn.config(state=DISABLED)
        self.stop_btn.config(state=NORMAL)
        self.code_editor.config(state=DISABLED)

        # 清空日志
        self.log_text.config(state=NORMAL)
        self.log_text.delete("1.0", tk.END)
        self.log_text.config(state=DISABLED)

        # 记录执行开始
        if self.current_file:
            self.log(f"开始执行脚本: {os.path.basename(self.current_file)}", "command")
        else:
            self.log("开始执行临时脚本", "command")

        # 在后台线程中执行脚本
        threading.Thread(target=self.execute_script, daemon=True).start()

    def execute_script(self):
        """执行脚本的实际函数"""
        try:
            # 获取脚本内容
            script_content = self.code_editor.get("1.0", tk.END)

            # 创建临时文件或使用现有文件
            if self.current_file and os.path.exists(self.current_file):
                script_path = self.current_file
            else:
                # 创建临时文件
                with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as temp_file:
                    temp_file.write(script_content)
                    script_path = temp_file.name

            # 构建命令
            command = [self.python_interpreter, script_path]
            self.log(f"执行命令: {' '.join(command)}", "command")

            # 执行脚本
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                bufsize=1,
                universal_newlines=True
            )

            # 存储进程引用，用于停止操作
            self.current_process = process

            # 读取标准输出
            def read_stdout():
                for line in process.stdout:
                    if not self.executing:
                        break
                    self.log(line.strip())

            # 读取标准错误
            def read_stderr():
                for line in process.stderr:
                    if not self.executing:
                        break
                    self.log(line.strip(), "error")

            # 启动线程读取输出
            stdout_thread = threading.Thread(target=read_stdout, daemon=True)
            stderr_thread = threading.Thread(target=read_stderr, daemon=True)

            stdout_thread.start()
            stderr_thread.start()

            # 等待进程完成
            while self.executing and process.poll() is None:
                threading.Event().wait(0.1)

            # 如果是被停止的，终止进程
            if self.executing and process.poll() is None:
                process.terminate()
                try:
                    process.wait(timeout=2)
                except subprocess.TimeoutExpired:
                    process.kill()

            # 等待输出线程完成
            stdout_thread.join()
            stderr_thread.join()

            # 检查返回码
            return_code = process.poll()
            if return_code == 0:
                self.log("脚本执行完成", "success")
            else:
                self.log(f"脚本执行失败，返回码: {return_code}", "error")

        except Exception as e:
            self.log(f"执行过程出错: {str(e)}", "error")
            self.log(traceback.format_exc(), "error")
        finally:
            # 清理临时文件
            if not self.current_file or not os.path.exists(self.current_file):
                try:
                    os.unlink(script_path)
                except:
                    pass

            # 恢复UI状态
            self.executing = False
            self.after(0, lambda: self.run_btn.config(state=NORMAL))
            self.after(0, lambda: self.stop_btn.config(state=DISABLED))
            self.after(0, lambda: self.code_editor.config(state=NORMAL))
            self.after(0, lambda: self.update_status("就绪"))

    def stop_script(self):
        """停止脚本执行"""
        if not self.executing:
            return

        if messagebox.askyesno("确认停止", "确定要停止脚本执行吗？"):
            self.log("用户请求停止脚本执行", "warning")
            self.executing = False
            self.update_status("正在停止脚本...")

    def on_close(self):
        """窗口关闭事件处理"""
        # 如果正在执行，先停止
        if self.executing:
            messagebox.showinfo("提示", "请先停止脚本执行再关闭窗口")
            return

        # 检查是否有未保存的修改
        if self.modified:
            response = messagebox.askyesnocancel(
                "保存提示",
                "当前文件有未保存的修改，是否保存？"
            )
            if response is None:  # 取消
                return
            if response:  # 是
                if not self.save_file():
                    return

        # 关闭窗口
        self.destroy()

    def create_widgets(self):
        pass
